package exteditapp.command;

import java.io.DataInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.ConnectException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.SocketTimeoutException;
import java.net.URL;

import exteditapp.FileDefault;
import exteditapp.state.StateError;

/**
 * Downloads file from plone server. 
 * Gets file with it's metadata and filters it – metadata is used only for informational purposes.
 * @author Arturas Smorgun, arturas@smorgun.lt
 */
public class CommandDownload extends ICommand {

	/**
	 * Default constructor
	 */
	public CommandDownload(FileDefault file){
		super(file);
	}

	/**
	 * Execution of download procedure
	 * @return true on success and false otherwise
	 * @throws IOException 
	 */
	public Boolean execute() {
		
		/**
		 * Begin of execution.
		 */
		
		execution = true;
		
		/**
		 * Download a file.
		 */
		
		int retryNumber = 0;
		try{
			URL myUrl = new URL(file.getDownloadURL());
			HttpURLConnection connection = (HttpURLConnection) myUrl.openConnection();
			connection.setConnectTimeout(3000);
			connection.setReadTimeout(3000);
			connection.setDefaultUseCaches(false);
			connection.setRequestProperty("Cache-Control","no-store,max-age=0,no-cache");
			connection.setRequestProperty("Expires", "0");
			connection.setRequestProperty("Pragma", "no-cache");
			connection.setDoInput(true);
			connection.setDoOutput(false);
			connection.setUseCaches(false);
			if(connection.getResponseCode() == 200){
				FileOutputStream out;
				
				try{
					String fileName = file.getName();
					File temp = File.createTempFile("extedit", fileName);
					temp.deleteOnExit();
					temp.setReadable(true, false);
					temp.setWritable(true, false);
					file.setFileAsObject(temp);
					out = new FileOutputStream(temp);
				}
				catch(IOException e){
					StateError newState = new StateError(this.file.getCurrentState().getIterator(), this.file);
					newState.setup("FILE SYSTEM ERROR");
					this.file.setCurrentState(newState);
					return false;
				}
					
				int fileSize = Integer.parseInt(connection.getHeaderField("content-length"));
				int counter = 0;
				int currentCounter = 0;
				DataInputStream in = new DataInputStream(connection.getInputStream());
				byte ch[] = new byte[1024];
				while((currentCounter = in.read(ch)) > 0){
					counter += currentCounter;
					try{
						out.write(ch, 0, currentCounter);
					}
					catch(IOException e){
						StateError newState = new StateError(this.file.getCurrentState().getIterator(), this.file);
						newState.setup("FILE SYSTEM ERROR");
						this.file.setCurrentState(newState);
						return false;
					}
					//counter += ch.length;
					file.getCurrentState().setProgress(counter * 100 / fileSize);
				}
				out.close();
				file.getModule().getInvoker().setExecution(false);
			}
			else{
			}
			connection.disconnect();
		}
		
		/**
		 * Catches passed invalid URL string.
		 */
		
		catch(MalformedURLException e){
			StateError newState = new StateError(this.file.getCurrentState().getIterator(), this.file);
			newState.setup("FATAL SYSTEM ERROR");
			this.file.setCurrentState(newState);
			return false;
		}
		
		/**
		 * Catches lost request, request rejection or server response error
		 */
		
		catch(ConnectException e){
			StateError newState = new StateError(this.file.getCurrentState().getIterator(), this.file);
			newState.setup("SERVER NOT FOUND");
			return false;
		}
		
		/**
		 * Catches network timeout - tries to retry request 5 times before showing error message
		 */
		
		catch(SocketTimeoutException e){
			retryNumber++;
			if(retryNumber > 5){
				StateError newState = new StateError(this.file.getCurrentState().getIterator(), this.file);
				newState.setup("CONNECTION TIMEOUT");
				this.file.setCurrentState(newState);
				return false;
			}
		}
		
		/**
		 * Catches IO exceptions (usually - everything what related with connection errors)
		 */
		
		catch(IOException e){
			StateError newState = new StateError(this.file.getCurrentState().getIterator(), this.file);
			newState.setup("CONNECTION ERROR");
			this.file.setCurrentState(newState);
			return false;
		}
		
		/**
		 * End of execution.
		 * Mark in Execution invoker, that this thread is almost ended it's workand next execu-
		 * tion (from queue) can be executed.
		 */
		
		execution = false;
		file.getModule().getInvoker().setExecution(execution);
		return false;
	}

	/**
	 * Method for command execution invocation in separated thread
	 */
	public void run() {
		execute();
	}

}